package math;

import java.util.Random;

public class Maths 
{

    private static final Random gen = new Random();
    
    public static final float PI = (float) Math.PI;
    public static final float PI_OVER_2 = (float) (Math.PI * 0.5d);
    public static final float PI_OVER_180 = (float) (Math.PI / 180.0d);
    
    public static final float PI_TIMES_2 = (float) (2.0d * Math.PI);
    
    public static final float SQRT_2_OVER_2 = (float) (Math.sqrt(2.0d) * 0.5d);
    
    public static final float ONE_OVER_3 = (float) (1.0d / 3.0d);
    public static final float ONE_OVER_180 = (float) (1.0d / 180.0d);
    public static final float ONE_OVER_255 = (float) (1.0d / 255.0d);
    public static final float ONE_OVER_1050 = (float) (1.0d / 1050.0d);
    public static final float ONE_OVER_1680 = (float) (1.0d / 1680.0d);


    private Maths() 
    {
    }
    
    public static float lerp(float t, float y0, float y1) 
    {
        return (1 - t) * y0 + t * y1;
    }
    
    public static float lerpf(float x, float x0, float x1, float y0, float y1) 
    {
        float dydx = (y1 - y0) / (x1 - x0);
        return (y0 + (x - x0) * dydx);
    }

    public static int lerpi(float x, float x0, float x1, float y0, float y1) 
    {
        float dydx = (y1 - y0) / (x1 - x0);
        return (int)(y0 + (x - x0) * dydx);
    }
    
    public static int lcm(int a, int b, int c) 
    {
        int t;
        if (a < b) // Now a >= b
        {
            t = b;  b = a;  a = t;
        }
        if (a < c) // Now a >= b and a >= c
        {
            t = c;  c = a;  a = t;
        }
        if (b < c) // Now a >= b >= c
        {
            t = c;  c = b;  b = t;
        }
        int multiple = 1;
        int remb, remc;
        int multa = (multiple * a);
        remb = multa % b;
        remc = multa % c;
        while (remb != 0 || remc != 0) 
        {
            multiple++;
            multa = (multiple * a);
            remb = multa % b;
            remc = multa % c;
        }
        return multa;
    }
    
    public static float mod(float num, float denom) 
    {
       if (num < 0) 
       {
           return (num % denom) + denom;
       } 
       else 
       {
           return (num % denom);
       }
    }
    
    public static int modi(int num, int denom) 
    {
       if (num < 0) 
       {
           return (num % denom) + denom;
       } 
       else 
       {
           return (num % denom);
       }
    }

    public static int randomInt(int limit) 
    {
        return gen.nextInt(limit);
    }

    public static int nearestInt(float num) 
    {
        if (num >= 0) 
        {
            return (int) (num + 0.5f);
        } 
        else 
        {
            return (int) (num - 0.5f);
        }
    }

    public static int floor(float num) 
    {
        if (num >= 0) 
        {
            return (int) num;
        } 
        else 
        {
            return (int) (num - 1);
        }
    }

    public static float log(float base, float num) 
    {
        return (ln(num) / ln(base));
    }

    public static float log10(float num) 
    {
        return (float) Math.log10(num);
    }

    public static float ln(float num) 
    {
        return (float) Math.log(num);
    }

    public static float cap(float num, float low, float high) 
    {
        float n = num;
        if (n < low) 
        {
            n = low;
        } 
        else if (n > high) 
        {
            n = high;
        }
        return n;
    }

    public static int capi(int num, int low, int high) 
    {
        int n = num;
        if (n < low) 
        {
            n = low;
        } 
        else if (n > high) 
        {
            n = high;
        }
        return n;
    }

    public static float pow(float base, float exp) 
    {
        return (float) Math.pow(base, exp);
    }

    public static float powIter(float base, float exp) 
    {
        float total = base;
        for (int i = 1; i < exp; i++) 
        {
            total *= base;
        }
        return total;
    }

    public static float random() 
    {
        float randomUnit = randomUnit();
        if (randomUnit > 0.5) 
        {
            return randomUnit() * Float.MAX_VALUE;
        } 
        else 
        {
            return -randomUnit() * Float.MAX_VALUE;
        }
    }

    public static float randomPos() 
    {
        float temp = random();
        if (temp < 0f) 
        {
            temp *= -1f;
        }
        return temp;
    }

    public static float randomUnit() 
    {
        return (float) (gen.nextDouble());
    }

    public static float randomNegToPosOne() 
    {
        return (float) (gen.nextDouble() * 2 - 1);
    }

    public static int min(int a, int b) 
    {
        return Math.min(a, b);
    }

    public static int max(int a, int b) 
    {
        return Math.max(a, b);
    }

    public static int min(int a, int b, int c) 
    {
        return min(min(a, b), c);
    }

    public static int max(int a, int b, int c) 
    {
        return max(max(a, b), c);
    }

    public static int min(int a, int b, int c, int d) 
    {
        return min(min(a, b), min(c, d));
    }

    public static int max(int a, int b, int c, int d) 
    {
        return max(max(a, b), max(c, d));
    }

    public static float min(float a, float b) 
    {
        return Math.min(a, b);
    }

    public static float max(float a, float b) 
    {
        return Math.max(a, b);
    }

    public static float min(float a, float b, float c) 
    {
        return min(min(a, b), c);
    }

    public static float max(float a, float b, float c) 
    {
        return max(max(a, b), c);
    }

    public static float min(float a, float b, float c, float d) 
    {
        return min(min(a, b), min(c, d));
    }

    public static float max(float a, float b, float c, float d) 
    {
        return max(max(a, b), max(c, d));
    }

    public static float cosRad(float t) 
    {
        return (float) Math.cos(t);
    }

    public static float cosDeg(float t) 
    {
        return (float) Math.cos(t * PI_OVER_180);
    }

    public static float sinRad(float t) 
    {
        return (float) Math.sin(t);
    }

    public static float sinDeg(float t) 
    {
        return (float) Math.sin(t * PI_OVER_180);
    }

    public static float tanRad(float t) 
    {
        return (float) Math.tan(t);
    }

    public static float tanDeg(float t) 
    {
        return (float) Math.tan(t * PI_OVER_180);
    }

    public static float atanRad(float y, float x) 
    {
        return (float) Math.atan2(y, x);
    }

    public static float atanDeg(float y, float x) 
    {
        return atanRad(y, x) * PI_OVER_180;
    }

    public static float acosRad(float t) 
    {
        return (float) Math.acos(t);
    }

    public static float acosDeg(float t) 
    {
        return acosRad(t) * PI_OVER_180;
    }

    public static float asinRad(float t) 
    {
        return (float) Math.asin(t);
    }

    public static float asinDeg(float t) 
    {
        return asinRad(t) * PI_OVER_180;
    }

    public static float sq(float base) 
    {
        return pow(base, 2);
    }

    public static float sqrt(float base) 
    {
        return (float) Math.sqrt(base);
    }

    public static float sqrt(double base) 
    {
        return (float) Math.sqrt(base);
    }

    public static float abs(float num) 
    {
        if (num < 0) 
        {
            return -num;
        }
        return num;
    }

    public static int abs(int num) 
    {
        if (num < 0) 
        {
            return -num;
        }
        return num;
    }

    public static float toRadians(float num) 
    {
        return (float) Math.toRadians(num);
    }

    public static float toDegrees(float num) 
    {
        return (float) Math.toDegrees(num);
    }

    public static float[][] multMatrix(float[][] a, float[][] b) 
    {
        if (a[0].length != b.length) 
        {
            return null;
        }
        int rowResult = a.length;
        int colResult = b[0].length;
        float[][] temp = new float[rowResult][colResult];
        for (int i = 0; i < temp.length; i++) 
        {
            for (int j = 0; j < temp[0].length; j++) 
            {
                float sum = getDotProduct(a, b, i, j);
                temp[i][j] = sum;
            }
        }
        return temp;
    }
    
    public static float[] multMat4(float[] a, float[] b) 
    {
        if (a.length != b.length) 
        {
            return null;
        }
        float[] result = new float[a.length];
        for (int i = 0; i < 4; i++) 
        {
            for (int j = 0; j < 4; j++) 
            {
                result[i + 4 * j] = getDotProduct4(a, b, i, j);
            }
        }
        return result;
    }
    
    public static float[] multMat3(float[] a, float[] b) 
    {
        if (a.length != b.length) 
        {
            return null;
        }
        float[] result = new float[a.length];
        for (int i = 0; i < 3; i++) 
        {
            for (int j = 0; j < 3; j++) 
            {
                result[i + 3 * j] = getDotProduct3(a, b, i, j);
            }
        }
        return result;
    }
    
    private static float getDotProduct4(float[] a, float[] b, int rowIndex, int colIndex) 
    {
        float sum = 0;
        for (int i = 0; i < 4; i++) 
        {
            sum += (a[rowIndex + 4 * i] * b[i + 4 * colIndex]);
        }
        return sum;
    }
    
    private static float getDotProduct3(float[] a, float[] b, int rowIndex, int colIndex) 
    {
        float sum = 0;
        for (int i = 0; i < 3; i++) 
        {
            sum += (a[rowIndex + 3 * i] * b[i + 3 * colIndex]);
        }
        return sum;
    }

    private static float getDotProduct(float[][] a, float[][] b, int rowIndex, int colIndex) 
    {
        float sum = 0;
        for (int i = 0; i < a[0].length; i++) 
        {
            sum += (a[rowIndex][i] * b[i][colIndex]);
        }
        return sum;
    }

    public static boolean bBox2DContainsExclusive(float l, float r, float b, float t, float xpos, float ypos) 
    {
        if (xpos <= l || xpos >= r) 
        {
            return false;
        }
        if (ypos <= b || ypos >= t) 
        {
            return false;
        }
        return true;
    }

    public static boolean bBox2DContainsExclusivec(float cx, float cy, float wt, float ht, float xpos, float ypos) 
    {
        return bBox2DContainsExclusive(cx - wt * 0.5f, cx + wt * 0.5f, cy - ht * 0.5f, cy + ht * 0.5f, xpos, ypos);
    }

}
